package com.ikun.admin.modules.noauth.iservice.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ikun.admin.modules.auth.constant.SecurityConstant;
import com.ikun.admin.modules.auth.service.TokenAboutRedisService;
import com.ikun.admin.modules.noauth.iservice.ICommonService;
import com.ikun.admin.modules.noauth.vo.DictVo;
import com.ikun.admin.modules.noauth.vo.ForgotPasswordVo;
import com.ikun.admin.modules.noauth.vo.LoginByCodeVo;
import com.ikun.admin.modules.noauth.vo.ReFreshVo;
import com.ikun.admin.modules.sys.entity.Dict;
import com.ikun.admin.modules.sys.entity.LogLogin;
import com.ikun.admin.modules.sys.entity.User;
import com.ikun.admin.modules.sys.iservice.IAclService;
import com.ikun.admin.modules.sys.service.DictService;
import com.ikun.admin.modules.sys.service.LogLoginService;
import com.ikun.admin.modules.sys.service.UserService;
import com.ikun.admin.modules.sys.vo.RoleCommonVo;
import com.ikun.common.constant.RedisConstant;
import com.ikun.common.entity.SecurityUserInfo;
import com.ikun.common.exception.MyException;
import com.ikun.common.manager.RedisManager;
import com.ikun.common.message.SendEmail;
import com.ikun.common.message.SendMessage;
import com.ikun.common.result.Result;
import com.ikun.common.utils.CaptchaUtil;
import com.ikun.common.utils.DataCopy;
import com.ikun.common.utils.HttpContextUtils;
import com.ikun.common.utils.RandomStr;
import com.ikun.common.validate.constant.RegularPatternType;
import com.ikun.common.validate.group.UpdateGroup;
import com.ikun.common.validate.validator.ValidateExecutor;
import com.ikun.starters.ip2region.IP2regionService;
import com.ikun.starters.jwt.impl.JwtMemberDataService;
import com.ikun.starters.jwt.impl.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName ICommonServiceImpk
 * @Description:
 * @CreateDate: 2024/4/20 11:09
 */
@Service
public class ICommonServiceImpl implements ICommonService {
    private static Map<Integer, String> codeTypeMap = new HashMap<>();

    static {
        codeTypeMap.put(1, RedisConstant.FORGET_CODE_PREFIX);
        codeTypeMap.put(2, RedisConstant.LOGIN_BY_CODE_PREFIX);
        codeTypeMap.put(3, RedisConstant.OTHER_CODE_PREFIX);
    }

    @Autowired
    private SendMessage sendMessage;
    @Autowired
    private SendEmail sendEmail;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisManager redisManager;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private IP2regionService ip2regionService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private LogLoginService logLoginService;
    @Autowired
    private IAclService iAclService;
    @Autowired
    private JwtMemberDataService jwtMemberDataService;

    @Autowired
    private DictService dictService;

    @Autowired
    private TokenAboutRedisService tokenAboutRedisService;

    /**
     * 验证码接口
     *
     * @param codeNumber
     * @param type
     * @return
     */
    @Override
    public Result<String> getCode(String codeNumber, Integer type) {
        //校验
        User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getMobile, codeNumber).or().eq(User::getEmail, codeNumber));
        if (Objects.isNull(user)) {
            throw new MyException("该号码未在本系统中绑定");
        }
        //生成随机验证码

        String code = RandomStr.generateVerificationCode(6, false);
        if (codeNumber.matches(RegularPatternType.CHINE_PHONE.getPattern())) {
            //todo 手机号用不了啦
            sendMessage.sendVerificationCode(codeNumber, code);
        }
        if (codeNumber.matches(RegularPatternType.COMMON_EMAIL.getPattern())) {
            //邮箱号
            sendEmail.sendTextEmail(codeNumber, "验证码", "你的验证码是:" + code + "有效时长两分半,请及时验证");
        }
        //保存 redis
        redisManager.addString(codeTypeMap.get(type) + codeNumber, code, 150, TimeUnit.SECONDS);
        return new Result<String>().success("你的验证码已经发送,请及时接收,有效时长两分半");
    }

    @Override
    public Result<String> checkCode(ForgotPasswordVo forgotPasswordVo) {

        String code_ = redisManager.getString(RedisConstant.FORGET_CODE_PREFIX + forgotPasswordVo.getCodeNumber());
        if (Objects.isNull(code_)) {
            throw new MyException("验证码已经过期,请重新发送");
        } else if (!Objects.equals(forgotPasswordVo.getCode(), code_)) {
            throw new MyException("验证码错误,请重新输入");
        } else {
            return new Result<String>().success("验证码正确");
        }

    }


    @Override
    public void getImgCode(HttpServletResponse response, HttpSession session) throws IOException {
        String code = RandomStr.generateVerificationCode(4, false);
        byte[] bytes = CaptchaUtil.generateCaptchaImage(code);
        response.setContentType("image/png");
        session.setAttribute("code", code);
        response.getOutputStream().write(bytes);
    }

    @Override
    public Result<String> checkImgCode(ForgotPasswordVo forgotPasswordVo, HttpSession session) {
        if (Objects.isNull(forgotPasswordVo.getUsername())) {
            throw new MyException("请输入账号");
        }
        User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getUsername, forgotPasswordVo.getUsername()));
        if (Objects.isNull(user)) {
            throw new MyException("该账号未在本平台注册,请先注册哦!");
        }
        String code = (String) session.getAttribute("code");
        if (!Objects.equals(code, forgotPasswordVo.getImgCode())) {
            throw new MyException("验证码错误");
        }


        return new Result<String>().success("验证码正确");
    }

    @Override
    @Transactional
    public Result<String> updatePasswordByCode(ForgotPasswordVo forgotPasswordVo) {
        ValidateExecutor.execute(forgotPasswordVo, UpdateGroup.class);

        if (!Objects.equals(forgotPasswordVo.getPassword(), forgotPasswordVo.getConfirmPassword())) {
            throw new MyException("两次密码不一致");
        }
        userService.update(new UpdateWrapper<User>().lambda()
                .eq(User::getUsername, forgotPasswordVo.getUsername())
                .eq(User::getMobile, forgotPasswordVo.getCodeNumber()).or()
                .eq(User::getEmail, forgotPasswordVo.getCodeNumber())
                .set(User::getPassword, passwordEncoder.encode(forgotPasswordVo.getPassword())));
        return new Result<String>().success("操作成功");
    }

    @Override
    public Result<SecurityUserInfo> loginByCode(LoginByCodeVo loginByCodeVo) {

        //验证码校验
        this.commonCheckCode(loginByCodeVo.getCodeNumber(), loginByCodeVo.getCodeType(), loginByCodeVo.getCode());
        //获取登录数据
        String codeNumber = loginByCodeVo.getCodeNumber();
        User loginUser = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getMobile, codeNumber)
                .or()
                .eq(User::getEmail, codeNumber));

        SecurityUserInfo securityUserInfo = this.buildLoginUserInfo(loginUser);
        return new Result<SecurityUserInfo>().success(securityUserInfo);

    }

    @Override
    public Result<String> commonCheckCode(String codeNumber, Integer codeType, String code) {
        String code_ = redisManager.getString(codeTypeMap.get(codeType) + codeNumber);
        if (Objects.isNull(code_)) {
            throw new MyException("验证码已过期");

        }
        if (!Objects.equals(code_, code)) {
            throw new MyException("验证码错误");
        }
        return new Result<String>().success("验证码正确");
    }

    @Override
    public Result<String> refreshToken(ReFreshVo reFreshVo) {
        String token = tokenService.createToken(String.valueOf(reFreshVo.getId()), reFreshVo.getUsername(), null);
        //角色
        List<RoleCommonVo> roleList = iAclService.selectRoleByUser(reFreshVo.getId());
        //权限
        List<String> permList = iAclService.selectMenuByUser(reFreshVo.getId());
        List<String> roleAndPermsList = new ArrayList<>();
        for (int i = 0; i < roleList.size(); i++) {
            roleAndPermsList.add(SecurityConstant.ROLE_PREFIX + roleList.get(i).getName());
        }
        roleAndPermsList.addAll(permList);

        // 储存 权限信息
        redisManager.addString(token, JSON.toJSONString(roleAndPermsList), 30, TimeUnit.HOURS);
        return new Result<String>().success(token);
    }

    @Override
    public Result<List<DictVo>> selectDict(String cluster) {
        List<Dict> dictList = dictService.list(new QueryWrapper<Dict>().lambda().eq(Dict::getCluster, cluster));

        List<DictVo> dictVoList = DataCopy.copyToListObj(dictList, DictVo.class);

        return new Result<List<DictVo>>().success(dictVoList);
    }

    /**
     * 构建登录信息
     *
     * @param loginUser
     * @return
     */
    private SecurityUserInfo buildLoginUserInfo(User loginUser) {
        // 记录登录地址信息
        String ipAddr = this.recordLoginInfo(loginUser);
        // 获取上一次登录信息
        LogLogin logLogin = logLoginService.getOne(new QueryWrapper<LogLogin>().lambda().eq(LogLogin::getCreator, loginUser.getId()).last("limit 1"));
        // 封装权限和角色信息
        Long id = loginUser.getId();
        SecurityUserInfo securityUserInfo = new SecurityUserInfo();
        securityUserInfo.setId(id);
        securityUserInfo.setUsername(loginUser.getUsername());
        securityUserInfo.setPassword(loginUser.getPassword());
        securityUserInfo.setNickName(loginUser.getNickname());
        securityUserInfo.setHeadUrl(loginUser.getHeadUrl());
        securityUserInfo.setIpAddr(ipAddr);
        securityUserInfo.setLastLoginTime(Objects.isNull(logLogin) ? LocalDateTime.now() : logLogin.getCreateTime());
        //接下来通过用户id查询用户的权限 用户id查询用户的角色
        List<RoleCommonVo> roleList = iAclService.selectRoleByUser(loginUser.getId());
        //权限
        List<String> permList = iAclService.selectMenuByUser(loginUser.getId());
        List<String> roleAndPermsList = new ArrayList<>();
        for (int i = 0; i < roleList.size(); i++) {
            roleAndPermsList.add(SecurityConstant.ROLE_PREFIX + roleList.get(i).getName());
        }
        //放权限
        securityUserInfo.setRoleNameList(roleList.stream().map(item -> item.getName()).collect(Collectors.toList()));
        roleAndPermsList.addAll(permList);

        //生成token
        String token = tokenService.createToken(String.valueOf(id), loginUser.getUsername(), null);

        // 储存 权限信息
        tokenAboutRedisService.setAboutToken(RedisConstant.USER_PERM_PREFIX, token, roleAndPermsList, 12, TimeUnit.HOURS);
        tokenAboutRedisService.setAboutToken(RedisConstant.USER_ID_PREFIX, token, id, 12, TimeUnit.HOURS);
        //记录登录状态
        tokenAboutRedisService.addSetAboutToken(RedisConstant.ONLINE_USER_SET, String.valueOf(id));
        return securityUserInfo;


    }


    /**
     * 记录登录信息 并且返回登录地址
     *
     * @param user
     * @return
     * @throws IOException
     */
    private String recordLoginInfo(User user) {
        String publicIP = ip2regionService.getPublicIP();
        String ipAddr = ip2regionService.parseIp(publicIP);
        LogLogin logLogin = new LogLogin();
        String ip = HttpContextUtils.getIp();
        logLogin.setLoginIp(ip);
        logLogin.setCreator(user.getId());
        logLogin.setLoginAddr(ipAddr);
        return ipAddr;
    }

}
